/******************************************************************************
DDWnd.CPP
Handles all the drawing and display functions for the screen saver

******************************************************************************/


#define INITGUID

#include "stdafx.h"
#include "DDWnd.h"
#include <ddraw.h>

/******************************************************************************
>	CDDWnd::CDDWnd()

	Purpose:	This function Initialises all of the required variables for
				the screen saver.
******************************************************************************/
CDDWnd::CDDWnd()
{
	m_pDD = NULL;
	m_pDDPal = NULL;
	
	// Initialise all the surfces and descriptors
	m_pHostWindow = NULL;
	m_pDDSPrimary = NULL;
	m_pDDSBack = NULL;
}


/******************************************************************************
>	CDDWnd::~CDDWnd()

	Purpose:	Calls release to make sure that everything that needs to
				be shut down is in fact shut down
******************************************************************************/
CDDWnd::~CDDWnd()
{
	//release the dd surface
	Release();
}

/******************************************************************************
>	void CDDWnd::Release()

	Purpose:	This function releases all the direct draw components that
				we currently have allocated.
******************************************************************************/
void CDDWnd::Release()
{
	// this function cleans up all the remaing direct draw objects
	if (m_pDD==NULL)
		return;
	// reset the display mode
	m_pDD->RestoreDisplayMode();
	// libera todos los elementos
	RELEASE(m_pDDPal);
	RELEASE(m_pDDSBack);
	RELEASE(m_pDDSPrimary);
	RELEASE(m_pDD);
}


/******************************************************************************
>	BOOL CDDWnd::Create(CWnd* pWindow, int Width, int Height, 
									int Depth, int NumBackBuffers,
									DirectDrawMode Mode)

	Inputs:		pWindow -	The window to use when needed. In windowed mode,
							this will be the window to draw into.
				bFullScrren - True for full screen mode, false for windowed mode
				nWidth, nHeight - The width and height of the display to go for
							Values must be 0,0 if no mode change
				nDepth - The bpp of the display wanted
							FullScreen will take over the screen and flick
							to a screen mode the right width and height.
							Windowed will create the surfaces as well as a
							clipper to clip the surface if needed
	Purpose:	This function creates the direct draw and the direct3d devices,
				surfaces and the z buffer
******************************************************************************/

BOOL CDDWnd::Create(CWnd* pWindow, BOOL bFullScreen, int nWidth, int nHeight, int nDepth)
{
	// por seguridad, si se habia creado previamente
	if (m_pDD != NULL)
		// se restaura
		Release();

	//  Set up Capabilities variables
	HRESULT rval;
	DWORD CoopLevel;
	DDSURFACEDESC ddsd;			// the surface desc
	memset(&ddsd,0, sizeof(DDSURFACEDESC));
	ddsd.dwSize = sizeof(ddsd);
	DDSCAPS	ddscaps;		// The capabilities of the direct draw surface
	
	m_bFullScreen = bFullScreen;
	m_pHostWindow = pWindow;

	rval = DirectDrawCreate(NULL, &m_pDD, NULL); // Create Direct Draw Device
	if(rval!=DD_OK)
	{
		DISPLAY("DirectDrawCreate FAILED");
		DISPLAY(MyErrorToString(rval));
        return FALSE;
	}
	HWND hwnd = pWindow->GetSafeHwnd(); // Get the window handle for the window (full screen)
	
	CString strError;

	if (bFullScreen)
	{
		CoopLevel = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;			//Exclusive and full screen mode

		if(m_pDD->SetCooperativeLevel(hwnd, CoopLevel) != DD_OK)
		{
			DISPLAY("Get Cooplevel FAILED");
			return FALSE;
		}
	
		// Set the display mode
		if (nWidth != 0 && nHeight != 0)
		{
			if(m_pDD->SetDisplayMode(nWidth, nHeight, nDepth) != DD_OK)
			{
				DISPLAY("Set Display mode FAILED");
				return FALSE;
			}
		}
	
		// Set up the surface descriptor 
		ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;// | DDSD_HEIGHT | DDSD_WIDTH;
		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |	// Primary 
							  DDSCAPS_FLIP |			// Flippable
							  DDSCAPS_COMPLEX;								 
		ddsd.dwBackBufferCount = 1;			// Only one back buffer 
    
		// Create The primary surface
		rval = m_pDD->CreateSurface(&ddsd, &m_pDDSPrimary, NULL);
		if(rval != DD_OK)
		{
			DISPLAY("Primary surface Creation Error");
			DISPLAY(MyErrorToString(rval));
			return FALSE;
		}

		// Create the back buffer
		ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
		rval = m_pDDSPrimary->GetAttachedSurface(&ddscaps, &m_pDDSBack);
		if( rval != DD_OK )
		{
			DISPLAY("Cannot Attach Surface\n");
			DISPLAY(MyErrorToString(rval));
			return FALSE;
		}
		DISPLAY("CDDWnd fullscreen created");
	}
	else
	{
		CoopLevel = DDSCL_NORMAL;			//Exclusive and full screen mode
		if(m_pDD->SetCooperativeLevel(hwnd, CoopLevel) != DD_OK)
		{
			DISPLAY("Get Cooplevel FAILED");
			return FALSE;
		}
	
        ddsd.dwFlags = DDSD_CAPS;         
		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;          
		// Create The primary surface
		rval = m_pDD->CreateSurface(&ddsd, &m_pDDSPrimary, NULL);
		if(rval != DD_OK)
		{
			DISPLAY("Primary surface Creation Error");
			DISPLAY(MyErrorToString(rval));
			return FALSE;
		}

		CRect rectWindow;
		pWindow->GetClientRect(rectWindow);
		
		// Create back surface
		memset(&ddsd,0, sizeof(DDSURFACEDESC));
		ddsd.dwSize = sizeof(ddsd);
		ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
		ddsd.dwHeight = max(rectWindow.Height(),1);
		ddsd.dwWidth = max(rectWindow.Width(),1);
		rval = m_pDD->CreateSurface(&ddsd, &m_pDDSBack, NULL );
		if(rval != DD_OK)
		{
			DISPLAY("Back surface Creation Error");
			DISPLAY(MyErrorToString(rval));
			return FALSE;
		}

		// now create a DirectDrawClipper object
		rval = m_pDD->CreateClipper(0, &m_pClipper, NULL);          
		if(rval != DD_OK)         
		{             
			DISPLAY("Clipper Creation Error");
			DISPLAY(MyErrorToString(rval));
			return FALSE;         
		}          
		rval = m_pClipper->SetHWnd(0, hwnd);          
		if(rval != DD_OK)         
		{             
			DISPLAY("Can't set clipper window handle");
			DISPLAY(MyErrorToString(rval));
			return FALSE;         
		}          
		rval = m_pDDSPrimary->SetClipper(m_pClipper);
		if(rval != DD_OK)         
		{             
			DISPLAY("Can't attach clipper to front buffer");
			DISPLAY(MyErrorToString(rval));
			return FALSE;         
		}     
		DISPLAY("CDDWnd windowed created");
	}

	// obtiene las caracteristicas de la superficie
	memset(&ddsd,0, sizeof(DDSURFACEDESC));
	ddsd.dwSize = sizeof(ddsd);
	m_pDDSBack->GetSurfaceDesc(&ddsd);

	m_nWidth = ddsd.dwWidth;
	m_nHeight= ddsd.dwHeight;
	m_nBpp = ddsd.ddpfPixelFormat.dwRGBBitCount;

	if(m_nBpp <=8)
	{
		//Less than 256 colours we need a palette
		if(!CreatePalette())
		{
			DISPLAY("Create palete failed\n");
			DISPLAY(MyErrorToString(rval));
			return FALSE;
		}
	}

	return TRUE;
}



/******************************************************************************

>	BOOL CDDWnd::CreataPalette()

	Purpose:	create a palette for a 8 bit mode a attach it to all surfaces

******************************************************************************/
BOOL CDDWnd::CreatePalette()
{
/*    PALETTEENTRY        pe[256];
    int                 i;
    HRESULT             ddrval;

	// Create a Direct Draw Palette and associate it with our front buffer
    if(m_pDDPal == NULL)
    {
        for(i=0; i<128; i++) // Set the colours
        {
            pe[i+128].peRed = 2*i;
            pe[i+128].peGreen = 2*i;
            pe[i+128].peBlue = 2*i;
            pe[i+128].peFlags = PC_EXPLICIT;
        }
        ddrval = m_pDD->CreatePalette(DDPCAPS_8BIT, pe, &m_pDDPal, NULL); // Create the palette
        if(ddrval != DD_OK)
		{
			TRACE("Create Palette failed");
			TRACE(MyErrorToString(ddrval));
            return FALSE;
		}
		// Assign the palette to the primary surface
        ddrval = m_pDDSPrimary->SetPalette(m_pDDPal);
		if(ddrval != DD_OK)
		{
			TRACE("Set Palette in primary surface failed");
			TRACE(MyErrorToString(ddrval));
            return FALSE;
		}
    
		// Assign the palette to the back buffer
        ddrval = m_pDDSBack->SetPalette(m_pDDPal);
		if(ddrval != DD_OK)
		{
			TRACE("Set Palette on back failed");
			TRACE(MyErrorToString(ddrval));
            return FALSE;
		}
    }
*/
	return FALSE;
}


/******************************************************************************
>	char* CDDWnd::MyErrorToString(HRESULT error)

	Inputs:		error	-	The Error code 

	Purpose:	Returns a string describing the errors in the program
******************************************************************************/

char* CDDWnd::MyErrorToString(HRESULT error)
{
  switch(error)
  {
    case DD_OK:
    // Also includes D3D_OK and D3DRM_OK 
      return "No error.\0";
    case DDERR_ALREADYINITIALIZED:
      return "This object is already initialized.\0";
    case DDERR_BLTFASTCANTCLIP:
      return "Return if a clipper object is attached to the source surface passed into a BltFast call.\0";
    case DDERR_CANNOTATTACHSURFACE:
      return "This surface can not be attached to the requested surface.\0";
    case DDERR_CANNOTDETACHSURFACE:
      return "This surface can not be detached from the requested surface.\0";
    case DDERR_CANTCREATEDC:
      return "Windows can not create any more DCs.\0";
    case DDERR_CANTDUPLICATE:
      return "Can't duplicate primary & 3D surfaces, or surfaces that are implicitly created.\0";
    case DDERR_CLIPPERISUSINGHWND:
      return "An attempt was made to set a cliplist for a clipper object that is already monitoring an hwnd.\0";
    case DDERR_COLORKEYNOTSET:
      return "No src color key specified for this operation.\0";
    case DDERR_CURRENTLYNOTAVAIL:
      return "Support is currently not available.\0";
    case DDERR_DIRECTDRAWALREADYCREATED:
      return "A DirectDraw object representing this driver has already been created for this process.\0";
    case DDERR_EXCEPTION:
      return "An exception was encountered while performing the requested operation.\0";
    case DDERR_EXCLUSIVEMODEALREADYSET:
      return "An attempt was made to set the cooperative level when it was already set to exclusive.\0";
    case DDERR_GENERIC:
      return "Generic failure.\0";
    case DDERR_HEIGHTALIGN:
      return "Height of rectangle provided is not a multiple of reqd alignment.\0";
    case DDERR_HWNDALREADYSET:
      return "The CooperativeLevel HWND has already been set. It can not be reset while the process has surfaces or palettes created.\0";
    case DDERR_HWNDSUBCLASSED:
      return "HWND used by DirectDraw CooperativeLevel has been subclassed, this prevents DirectDraw from restoring state.\0";
    case DDERR_IMPLICITLYCREATED:
      return "This surface can not be restored because it is an implicitly created surface.\0";
    case DDERR_INCOMPATIBLEPRIMARY:
      return "Unable to match primary surface creation request with existing primary surface.\0";
    case DDERR_INVALIDCAPS:
      return "One or more of the caps bits passed to the callback are incorrect.\0";
    case DDERR_INVALIDCLIPLIST:
      return "DirectDraw does not support the provided cliplist.\0";
    case DDERR_INVALIDDIRECTDRAWGUID:
      return "The GUID passed to DirectDrawCreate is not a valid DirectDraw driver identifier.\0";
    case DDERR_INVALIDMODE:
      return "DirectDraw does not support the requested mode.\0";
    case DDERR_INVALIDOBJECT:
      return "DirectDraw received a pointer that was an invalid DIRECTDRAW object.\0";
    case DDERR_INVALIDPARAMS:
      return "One or more of the parameters passed to the function are incorrect.\0";
    case DDERR_INVALIDPIXELFORMAT:
      return "The pixel format was invalid as specified.\0";
    case DDERR_INVALIDPOSITION:
      return "Returned when the position of the overlay on the destination is no longer legal for that destination.\0";
    case DDERR_INVALIDRECT:
      return "Rectangle provided was invalid.\0";
    case DDERR_LOCKEDSURFACES:
      return "Operation could not be carried out because one or more surfaces are locked.\0";
    case DDERR_NO3D:
      return "There is no 3D present.\0";
    case DDERR_NOALPHAHW:
      return "Operation could not be carried out because there is no alpha accleration hardware present or available.\0";
    case DDERR_NOBLTHW:
      return "No blitter hardware present.\0";
    case DDERR_NOCLIPLIST:
      return "No cliplist available.\0";
    case DDERR_NOCLIPPERATTACHED:
      return "No clipper object attached to surface object.\0";
    case DDERR_NOCOLORCONVHW:
      return "Operation could not be carried out because there is no color conversion hardware present or available.\0";
    case DDERR_NOCOLORKEY:
      return "Surface doesn't currently have a color key\0";
    case DDERR_NOCOLORKEYHW:
      return "Operation could not be carried out because there is no hardware support of the destination color key.\0";
    case DDERR_NOCOOPERATIVELEVELSET:
      return "Create function called without DirectDraw object method SetCooperativeLevel being called.\0";
    case DDERR_NODC:
      return "No DC was ever created for this surface.\0";
    case DDERR_NODDROPSHW:
      return "No DirectDraw ROP hardware.\0";
    case DDERR_NODIRECTDRAWHW:
      return "A hardware-only DirectDraw object creation was attempted but the driver did not support any hardware.\0";
    case DDERR_NOEMULATION:
      return "Software emulation not available.\0";
    case DDERR_NOEXCLUSIVEMODE:
      return "Operation requires the application to have exclusive mode but the application does not have exclusive mode.\0";
    case DDERR_NOFLIPHW:
      return "Flipping visible surfaces is not supported.\0";
    case DDERR_NOGDI:
      return "There is no GDI present.\0";
    case DDERR_NOHWND:
      return "Clipper notification requires an HWND or no HWND has previously been set as the CooperativeLevel HWND.\0";
    case DDERR_NOMIRRORHW:
      return "Operation could not be carried out because there is no hardware present or available.\0";
    case DDERR_NOOVERLAYDEST:
      return "Returned when GetOverlayPosition is called on an overlay that UpdateOverlay has never been called on to establish a destination.\0";
    case DDERR_NOOVERLAYHW:
      return "Operation could not be carried out because there is no overlay hardware present or available.\0";
    case DDERR_NOPALETTEATTACHED:
      return "No palette object attached to this surface.\0";
    case DDERR_NOPALETTEHW:
      return "No hardware support for 16 or 256 color palettes.\0";
    case DDERR_NORASTEROPHW:
      return "Operation could not be carried out because there is no appropriate raster op hardware present or available.\0";
    case DDERR_NOROTATIONHW:
      return "Operation could not be carried out because there is no rotation hardware present or available.\0";
    case DDERR_NOSTRETCHHW:
      return "Operation could not be carried out because there is no hardware support for stretching.\0";
    case DDERR_NOT4BITCOLOR:
      return "DirectDrawSurface is not in 4 bit color palette and the requested operation requires 4 bit color palette.\0";
    case DDERR_NOT4BITCOLORINDEX:
      return "DirectDrawSurface is not in 4 bit color index palette and the requested operation requires 4 bit color index palette.\0";
    case DDERR_NOT8BITCOLOR:
      return "DirectDrawSurface is not in 8 bit color mode and the requested operation requires 8 bit color.\0";
    case DDERR_NOTAOVERLAYSURFACE:
      return "Returned when an overlay member is called for a non-overlay surface.\0";
    case DDERR_NOTEXTUREHW:
      return "Operation could not be carried out because there is no texture mapping hardware present or available.\0";
    case DDERR_NOTFLIPPABLE:
      return "An attempt has been made to flip a surface that is not flippable.\0";
    case DDERR_NOTFOUND:
      return "Requested item was not found.\0";
    case DDERR_NOTLOCKED:
      return "Surface was not locked.  An attempt to unlock a surface that was not locked at all, or by this process, has been attempted.\0";
    case DDERR_NOTPALETTIZED:
      return "The surface being used is not a palette-based surface.\0";
    case DDERR_NOVSYNCHW:
      return "Operation could not be carried out because there is no hardware support for vertical blank synchronized operations.\0";
    case DDERR_NOZBUFFERHW:
      return "Operation could not be carried out because there is no hardware support for zbuffer blitting.\0";
    case DDERR_NOZOVERLAYHW:
      return "Overlay surfaces could not be z layered based on their BltOrder because the hardware does not support z layering of overlays.\0";
    case DDERR_OUTOFCAPS:
      return "The hardware needed for the requested operation has already been allocated.\0";
    case DDERR_OUTOFMEMORY:
      return "DirectDraw does not have enough memory to perform the operation.\0";
    case DDERR_OUTOFVIDEOMEMORY:
      return "DirectDraw does not have enough memory to perform the operation.\0";
    case DDERR_OVERLAYCANTCLIP:
      return "The hardware does not support clipped overlays.\0";
    case DDERR_OVERLAYCOLORKEYONLYONEACTIVE:
      return "Can only have ony color key active at one time for overlays.\0";
    case DDERR_OVERLAYNOTVISIBLE:
      return "Returned when GetOverlayPosition is called on a hidden overlay.\0";
    case DDERR_PALETTEBUSY:
      return "Access to this palette is being refused because the palette is already locked by another thread.\0";
    case DDERR_PRIMARYSURFACEALREADYEXISTS:
      return "This process already has created a primary surface.\0";
    case DDERR_REGIONTOOSMALL:
      return "Region passed to Clipper::GetClipList is too small.\0";
    case DDERR_SURFACEALREADYATTACHED:
      return "This surface is already attached to the surface it is being attached to.\0";
    case DDERR_SURFACEALREADYDEPENDENT:
      return "This surface is already a dependency of the surface it is being made a dependency of.\0";
    case DDERR_SURFACEBUSY:
      return "Access to this surface is being refused because the surface is already locked by another thread.\0";
    case DDERR_SURFACEISOBSCURED:
      return "Access to surface refused because the surface is obscured.\0";
    case DDERR_SURFACELOST:
      return "Access to this surface is being refused because the surface memory is gone. The DirectDrawSurface object representing this surface should have Restore called on it.\0";
    case DDERR_SURFACENOTATTACHED:
      return "The requested surface is not attached.\0";
    case DDERR_TOOBIGHEIGHT:
      return "Height requested by DirectDraw is too large.\0";
    case DDERR_TOOBIGSIZE:
      return "Size requested by DirectDraw is too large, but the individual height and width are OK.\0";
    case DDERR_TOOBIGWIDTH:
      return "Width requested by DirectDraw is too large.\0";
    case DDERR_UNSUPPORTED:
      return "Action not supported.\0";
    case DDERR_UNSUPPORTEDFORMAT:
      return "FOURCC format requested is unsupported by DirectDraw.\0";
    case DDERR_UNSUPPORTEDMASK:
      return "Bitmask in the pixel format requested is unsupported by DirectDraw.\0";
    case DDERR_VERTICALBLANKINPROGRESS:
      return "Vertical blank is in progress.\0";
    case DDERR_WASSTILLDRAWING:
      return "Informs DirectDraw that the previous Blt which is transfering information to or from this Surface is incomplete.\0";
    case DDERR_WRONGMODE:
      return "This surface can not be restored because it was created in a different mode.\0";
    case DDERR_XALIGN:
      return "Rectangle provided was not horizontally aligned on required boundary.\0";
    default:
      return "Unrecognized error value.\0";
  }
}


/******************************************************************************
>	void CDDWnd::UpdateScreen()

	Purpose:	Copy/Flip de back buffer to the primary one
******************************************************************************/

void CDDWnd::UpdateScreen()
{
	HRESULT rval;
	if (m_bFullScreen)
	{
		rval = m_pDDSPrimary->Flip(NULL, DDFLIP_WAIT);
		if(rval!=DD_OK)
		{
			DISPLAY("Flip FAILED");
			DISPLAY(MyErrorToString(rval));
		}
	}
	else
	{
		CRect rectWindow;
		m_pHostWindow->GetClientRect(rectWindow);
		m_pHostWindow->ClientToScreen(rectWindow);
//		m_pHostWindow->GetWindowRect(rectWindow);
		CString strError;
		strError.Format("CRect(%d, %d, %d, %d)",rectWindow.left, rectWindow.top, rectWindow.right,rectWindow.bottom);
		DISPLAY(strError);
		rval = m_pDDSPrimary->Blt(rectWindow, m_pDDSBack, NULL, DDBLT_WAIT, NULL);      
		if(rval!=DD_OK)
		{
			DISPLAY("Blt FAILED");
			DISPLAY(MyErrorToString(rval));
		}
	}
}

/******************************************************************************
>	void CDDWnd::CheckLost()

	Purpose:	Check if primary o back buffer are lost
******************************************************************************/

void CDDWnd::CheckLost()
{
	HRESULT rval;
	if (m_pDDSPrimary->IsLost() != DD_OK)
	{
		rval = m_pDDSPrimary->Restore();
		if(rval!=DD_OK)
		{
			DISPLAY("Primary surface restore FAILED");
			DISPLAY(MyErrorToString(rval));
		}
	}
	if (m_pDDSBack->IsLost() != DD_OK)
	{
		rval = m_pDDSBack->Restore();
		if(rval!=DD_OK)
		{
			DISPLAY("Back surface restore FAILED");
			DISPLAY(MyErrorToString(rval));
		}
	}
}

#ifdef _DEBUG
/******************************************************************************

>	void DISPLAY(char* x)

	Purpose:	Writes any errors to an error file

******************************************************************************/
void DISPLAY(LPCSTR x)
{
	FILE *fp;
	fp = fopen("Errors.txt", "a");
	fprintf(fp, x);
	fprintf(fp,"\n");
	fclose(fp);
}
#endif

